﻿using Dycox.Collections;
using Dycox.Linq;
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Xml.Linq;

namespace Dycox.ComponentModel
{
    public class ViewField : ViewObject, ICloneable
    {
        internal ViewField(XElement elt)
            : this(elt.GetAttrString("name").TrimToNull())
        {
            this._propertyName = elt.GetAttrString("property").TrimToNull();
            this._label = elt.GetAttrString("label").EmptyToNull();
            this._format = elt.GetAttrString("format").EmptyToNull();
            this._size = elt.GetAttrInt32("size") ?? 0;
            this._nullText = elt.GetAttrString("nullText").EmptyToNull();
            //this._category = ((string)elt.Attribute("category")).TrimToNull();
            this._description = ((string)elt.Attribute("description")).TrimToNull();
            this._halign = elt.GetAttrEnum<HAlign>("hAlign");
            this._valign = elt.GetAttrEnum<VAlign>("vAlign");
            this._aggregate = elt.GetAttrEnum<AggregationFunction>("aggregate");
            this._hideDefaultValue = elt.GetAttrBoolean("hideDefaultValue").GetValueOrDefault();

            XElement xe = elt.Element(ModelView.ViewNamespace + "matches");

            if (xe != null)
            {
                List<MatchSettings> list = new List<MatchSettings>();
                foreach (var melt in xe.Elements())
                {
                    MatchSettings ms = MatchSettings.Create(melt);
                    if (ms != null)
                        list.Add(ms);
                }

                if (list.Count > 0)
                    _matches = list.ToArray();
            }

        }

        internal ViewField(PropertyDescriptor property)
            : base(property.Name)
        {
            _propertyName = property.Name;
            _label = property.DisplayName;

            DisplayFormatAttribute dfa = (DisplayFormatAttribute)property.Attributes[typeof(DisplayFormatAttribute)];
            if (dfa != null)
            {
                _format = dfa.Format;
                _nullText = dfa.NullText;

            }

            TypeFlags tf = property.PropertyType.GetTypeFlags();
            if ((tf & TypeFlags.Integer) == TypeFlags.Integer ||
                (tf & TypeFlags.Float) == TypeFlags.Float && !string.IsNullOrEmpty(_format))
                _halign = Dycox.HAlign.Right;

            _property = property;

            Initialize();
        }

        internal void Initialize()
        {
            if (_property == null)
            {
                string name = _propertyName ?? Name;
                _property = FindProperty(View.ModelType, name);
            }

            if (_label == null)
            {
                if (_property != null)
                {
                    _label = _property.DisplayName;
                }
                else
                    _label = Name;
            }

            if (_property != null)
            {
                //if (_category == null)
                //{
                //    CategoryAttribute ca = (CategoryAttribute)_property.Attributes[typeof(CategoryAttribute)];
                //    if (ca != null && !ca.Equals(CategoryAttribute.Default)) _category = ca.Category;
                //}

                if (_description == null)
                {
                    _description = _property.Description;
                }

                var ska = (ModelPropertyAttribute)_property.Attributes[typeof(ModelPropertyAttribute)];
                if (ska != null) _usage = ska.Usage;
            }
            else
            {
                _usage = ModelPropertyUsage.Application;
            }
        }

        private PropertyDescriptor FindProperty(Type type, string name)
        {
            if (string.IsNullOrEmpty(name)) return null;

            string[] array = name.Split(Type.Delimiter);
            PropertyDescriptor p = null;

            foreach (string n in array)
            {
                p = TypeDescriptor.GetProperties(type)[n];
                if (p == null) break;

                type = p.PropertyType;
            }

            return p;
        }

        public ViewField(string name)
            : base(name)
        {
        }

        public bool IsMatch(object filter)
        {
            MatchSettings ms;

            return IsMatch(filter, out ms);
        }

        private bool IsMatch(object value, out MatchSettings ms)
        {
            if (_matches == null)
            {
                ms = null;
                return true;
            }

            if ( _matches.Length == 0)
            {
                ms = null;
                return value == null;
            }

            ms = _matches.FirstOrDefault(o => o.Test(value));
            return ms != null;
        }

        internal ViewField GetMatchedField(object value)
        {
            MatchSettings ms;
            
            if (IsMatch(value, out ms))
            {

                ViewField vf;

                if (ms != null)
                {
                    vf = new ViewField(Name);

                    //vf._category = _category;
                    vf._description = ms.Description ?? _description;
                    vf._format = ms.Format ?? _format;
                    vf._label = ms.Label ?? _label;
                    vf._nullText = ms.NullText ?? _nullText;
                    vf._property = _property;
                    vf._propertyName = _propertyName;
                    vf._size = ms.Size ?? _size;
                    vf._usage = _usage;
                    vf._hideDefaultValue = _hideDefaultValue;
                }
                else
                    vf = (ViewField)((ICloneable)this).Clone();

                return vf;
            }
            else
                return null;
        }

        object ICloneable.Clone()
        {
            ViewField vf = (ViewField)this.MemberwiseClone();

            vf._matches = null;
            vf.DeattachFromParent();

            return vf;
        }

        private string _nullText;

        public string NullText
        {
            get { return _nullText; }
            set { _nullText = value; }
        }

        private MatchSettings[] _matches;

        private VAlign _valign;

        public VAlign VAlign
        {
            get { return _valign; }
            set { _valign = value; }
        }

        private HAlign _halign;

        public HAlign HAlign
        {
            get { return _halign; }
            set { _halign = value; }
        }

        private AggregationFunction _aggregate;

        public AggregationFunction Aggregate
        {
            get { return _aggregate; }
            set { _aggregate = value; }
        }


        private string _propertyName;

        public string PropertyName
        {
            get { return _propertyName ?? Name; }
            set
            {
                _propertyName = value;
                throw new NotImplementedException();
            }
        }

        private PropertyDescriptor _property;

        public PropertyDescriptor Property
        {
            get { return _property; }
        }

        private string _label;

        public string Label
        {
            get
            {
                return _label;
            }
            set
            {
                _label = value;
            }
        }

        private string _format;

        public string Format
        {
            get { return _format; }
            set { _format = value; }
        }

        private int _size;

        public int Size
        {
            get { return _size; }
            set { _size = value; }
        }

        //private string _category;

        //public string Category
        //{
        //    get { return _category ?? string.Empty; }
        //    set { _category = value; }
        //}

        private bool _hideDefaultValue;

        public bool HideDefaultValue
        {
            get { return _hideDefaultValue; }
            set { _hideDefaultValue = value; }
        }

        private string _description;

        public string Description
        {
            get { return _description; }
            set { _description = value; }
        }

        private ModelPropertyUsage _usage;

        public ModelPropertyUsage Usage
        {
            get { return _usage; }
        }

        abstract class MatchSettings
        {
            public string Format { get; set; }

            public int? Size { get; set; }

            public string Label { get; set; }

            public string Description { get; set; }

            public string NullText { get; set; }

            public abstract bool Test(object value);

            public string Content { get; set; }

            internal static MatchSettings Create(XElement melt)
            {
                XName name = melt.Name;
                if (name.Namespace == ModelView.ViewNamespace)
                {
                    MatchSettings ms;
                    switch (name.LocalName)
                    {
                        case "literal":
                            ms = new LiteralMatchSettings();
                            ((LiteralMatchSettings)ms).Expression = (string)melt.Attribute("eval");
                            break;
                        case "condition":
                            ms = new ConditionalMatchSettings();
                            break;
                        default:
                            throw new NotSupportedException();
                    }

                    ms.Content = melt.Value;
                    ms.Description = (string)melt.Attribute("description");
                    ms.Format = (string)melt.Attribute("format");
                    ms.Label = (string)melt.Attribute("label");
                    ms.NullText = (string)melt.Attribute("nullText");
                    ms.Size = (int?)melt.Attribute("size");

                    return ms;
                }
                else
                    return null;
            }
        }

        sealed class LiteralMatchSettings : MatchSettings
        {
            public override bool Test(object value)
            {
                if (value == null)
                    return false;

                string text;

                if (value is string) text = (string)value;
                else
                {
                    if (!string.IsNullOrEmpty(Expression))
                    {
                        value = Utils.DataBinderUtil.Eval(value, Expression);
                        if (value == null) return false;
                    }

                    if (value is string) text = (string)value;
                    else text = value.ToString();
                }

                return text.Equals(Content, StringComparison.InvariantCultureIgnoreCase);
            }

            public string Expression { get; set; }
        }

        sealed class ConditionalMatchSettings : MatchSettings
        {
            Delegate _delegate;
            Type _type;

            private Delegate BuildExpresion(Type type)
            {
                if (_type != type || _delegate == null)
                {
                    _delegate = System.Linq.Dynamic.DynamicExpression.ParseLambda(type, typeof(bool), Content).Compile();
                    _type = type;
                }

                return _delegate;
            }

            public override bool Test(object value)
            {
                if (value == null || string.IsNullOrEmpty(Content))
                    return false;

                return (bool)BuildExpresion(value.GetType()).DynamicInvoke(value);
            }
        }

    }
}
